//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
//
// Routines to Bus Support the QCIbus
//
#include <windows.h>
#include <types.h>
#include <qcibus.h>
#include <CeBusCfg.h>

#include "qci.h"
#include "qci_private.h"

#ifdef DEBUG

DBGPARAM dpCurSettings = {
    _T("QCIBUS"), 
    {
        _T("Errors"), _T("Warnings"), _T("Init"), _T("Function"), 
        _T("Ioctl"), _T("Device"), _T("Activity"), _T(""),
        _T(""),_T(""),_T(""),_T(""),
        _T(""),_T(""),_T(""),_T("") 
    },
    MASK_ERROR | MASK_WARN | MASK_INIT 
};
 
#endif

SensorFolder::SensorFolder(LPCTSTR lpBusName,LPCTSTR lpTemplateRegPath,DWORD dwBusType, DWORD BusNumber,PCI_SLOT_NUMBER SlotNumber,HANDLE hParent)
:   DeviceFolder(lpBusName,lpTemplateRegPath,dwBusType,BusNumber,SlotNumber.u.bits.DeviceNumber,SlotNumber.u.bits.FunctionNumber, hParent)
{

}
SensorFolder::~SensorFolder()
{

}
// PCI Power Handling;
BOOL SensorFolder::SetPowerState(CEDEVICE_POWER_STATE newPowerState)
{
    return TRUE;
}

//-------------------------------------PCI Bus Driver -------------------------------------------------------
QciBus::QciBus(LPCTSTR lpActiveRegPath) : 
    DefaultBusDriver(lpActiveRegPath),
    m_DeviceKey(lpActiveRegPath)
{
    DEBUGMSG(ZONE_ENUM| ZONE_INIT,(TEXT("QciBus::QciBus (ActivateRegPath=%s)\r\n"),lpActiveRegPath));

    // Get DEVLOAD_DEVKEY_VALNAME name
    m_lpActiveRegPath = NULL;
    HANDLE hThisDevice = GetDeviceHandleFromContext(lpActiveRegPath);
    if(hThisDevice != NULL) {
        DEVMGR_DEVICE_INFORMATION di;
        memset(&di, 0, sizeof(di));
        di.dwSize = sizeof(di);
        if(GetDeviceInformationByDeviceHandle(hThisDevice, &di)) {
            DWORD dwKeyLen = wcslen(di.szDeviceKey) + 1;
            m_lpActiveRegPath = new TCHAR [dwKeyLen];
            if ( m_lpActiveRegPath) {
                wcscpy(m_lpActiveRegPath, di.szDeviceKey);
            }
            LPCWSTR pszName = wcschr(di.szBusName, L'\\');
            if(pszName != NULL) {
                pszName++;
                if(*pszName != 0) {
                    dwKeyLen = wcslen(pszName) + 1;
                    m_lpBusName = new TCHAR[dwKeyLen];
                    if (m_lpBusName) {
                        wcscpy(m_lpBusName, pszName);
                    }
                }
            }
        }
    }	

    m_lpStrInitParam = NULL;
    m_dwNumOfInitParam =0 ;
    m_dwDeviceIndex = 0;
}
QciBus::~QciBus()
{
    if (m_lpStrInitParam )
        delete [] m_lpStrInitParam ;
    if (m_lpActiveRegPath) 
        delete [] m_lpActiveRegPath;
}
#define BUSNAMEUNKNOWN TEXT("UnknownBus")
BOOL QciBus::Init()
{
    BOOL  bReturn = FALSE;

    if (!init_all())
        return FALSE;

    // Initialize InitParam
    if ( m_lpActiveRegPath==NULL || !m_DeviceKey.IsKeyOpened()) {
        DEBUGMSG(ZONE_ENUM|ZONE_ERROR,(TEXT("QciBus!Init RegOpenKeyEx() return FALSE!!!\r\n")));
        return FALSE;
    }
    if (m_lpStrInitParam == NULL ) {
        DWORD dwKeyLen=0;
        DWORD dwType;
        // BC. It is BC with RegEnum We may want remove this later
        // What this key contain multi-string that indicate the reigstry has such name will copy into active registry
        if (m_DeviceKey.RegQueryValueEx(DEVLOAD_REPARMS_VALNAME, &dwType,NULL,&dwKeyLen) && dwType== REG_MULTI_SZ && dwKeyLen!=0 ) {
            dwKeyLen = (dwKeyLen + sizeof(TCHAR) -1 ) / sizeof(TCHAR);
            m_lpStrInitParam = new TCHAR [ dwKeyLen ];
            if (m_lpStrInitParam) {
                if (m_DeviceKey.GetRegValue(DEVLOAD_REPARMS_VALNAME,(LPBYTE)m_lpStrInitParam,dwKeyLen * sizeof(TCHAR))) {
                    DWORD dwUnitCount= dwKeyLen;                        
                    for (LPTSTR lpCurPos = m_lpStrInitParam;
                            m_dwNumOfInitParam< MAX_INITPARAM && dwUnitCount!=0 && *lpCurPos!=0 ; 
                            m_dwNumOfInitParam++) {
                        m_lpInitParamArray[m_dwNumOfInitParam] = lpCurPos;
                        while (*lpCurPos!=0 && dwUnitCount!=0  ) {
                            lpCurPos++;
                            dwUnitCount --;
                        }
                        if (dwUnitCount) {
                            lpCurPos ++;
                            dwUnitCount --;
                        }
                        else 
                            break;
                    }
                            
                }
            }
        }
    }

    // Scan And Enum All the driver in registry.
    if ( GetDeviceHandle()!=NULL &&  m_DeviceKey.IsKeyOpened()) {
        DWORD dwKeyLen=0;
        if (!m_DeviceKey.GetRegValue(DEVLOAD_INTERFACETYPE_VALNAME,(PUCHAR) &m_dwBusType,sizeof(m_dwBusType))) {
            // No interface type defined
            m_dwBusType=InterfaceTypeUndefined;
        };
        if (!m_DeviceKey.GetRegValue(DEVLOAD_BUSNUMBER_VALNAME , (LPBYTE)&m_dwBusNumber,sizeof(m_dwBusNumber)))
            m_dwBusNumber=0;

        if ( AssignChildDriver() )
            return TRUE;
    };
    DEBUGMSG(ZONE_ENUM|ZONE_ERROR,(TEXT("-BusENUM!Init eturn FALSE!!!\r\n")));
    return FALSE;
    
};

BOOL QciBus::PostInit()
{
    BOOL bReturn = FALSE;

    if ( GetDeviceHandle()!=NULL &&  m_DeviceKey.IsKeyOpened()) {
        bReturn=ActiveAllChildDriver();
    };

    return bReturn;
}

DWORD QciBus::GetBusNamePrefix(__out_ecount(dwSizeInUnit) LPTSTR lpReturnBusName,DWORD dwSizeInUnit)
{
    if (m_lpBusName && lpReturnBusName &&  dwSizeInUnit) {
        DWORD dwCopyUnit = min(_tcslen(m_lpBusName) +1 , dwSizeInUnit);
        _tcsncpy(lpReturnBusName,m_lpBusName,dwCopyUnit);
        lpReturnBusName[dwCopyUnit-1]=0;
        return dwCopyUnit;
    }
    else
        return DefaultBusDriver::GetBusNamePrefix(lpReturnBusName,dwSizeInUnit);
}

BOOL QciBus::AssignChildDriver()
{
    DWORD NumSubKeys;
    DWORD MaxSubKeyLen;
    DWORD MaxClassLen;
    DWORD NumValues;
    DWORD MaxValueNameLen;
    DWORD MaxValueLen;

    // Get info on Template Key
    BOOL bSuccess = m_DeviceKey.RegQueryInfoKey(
                NULL,               // class name buffer (lpszClass)
                NULL,               // ptr to length of class name buffer (lpcchClass)
                NULL,               // reserved
                &NumSubKeys,        // ptr to number of sub-keys (lpcSubKeys)
                &MaxSubKeyLen,      // ptr to longest subkey name length (lpcchMaxSubKeyLen)
                &MaxClassLen,       // ptr to longest class string length (lpcchMaxClassLen)
                &NumValues,         // ptr to number of value entries (lpcValues)
                &MaxValueNameLen,  // ptr to longest value name length (lpcchMaxValueNameLen)
                &MaxValueLen,       // ptr to longest value data length (lpcbMaxValueData)
                NULL,               // ptr to security descriptor length
                NULL);              // ptr to last write time
                    
    if (!bSuccess) {
        DEBUGMSG(ZONE_ENUM|ZONE_ERROR,
            (TEXT("PCMCIA::RegCopyKey RegQueryInfoKey returned fails.\r\n")));
        return FALSE;
    }
    // Recurse for each sub-key
    
    for (DWORD Key = 0; Key < NumSubKeys; Key++) {
        // Get TKey sub-key according to Key
        WCHAR ValName[DEVKEY_LEN];
        DWORD ValLen = sizeof(ValName) / sizeof(WCHAR);
        if (! m_DeviceKey.RegEnumKeyEx(  Key, ValName, &ValLen, NULL,  NULL,  NULL, NULL)){
            DEBUGMSG(ZONE_ENUM,
                (TEXT("BusEnum::RegCopyKey RegEnumKeyEx(%d) returned Error\r\n"), ValName));
            break;
        }
        else {
            // Open sub-key under TKey
            CRegistryEdit TSubKey(m_DeviceKey.GetHKey(),ValName);
            if (!TSubKey.IsKeyOpened()) {
                DEBUGMSG(ZONE_ENUM|ZONE_ERROR,
                    (TEXT("QCIBUS::RegCopyKey RegOpenKeyEx(%s) returned Error\r\n"), ValName));
                
                continue;
            }
            else { // Get Bus Info.
                DDKPCIINFO dpi;
                dpi.cbSize = sizeof (DDKPCIINFO);                
                dpi.dwDeviceNumber = (DWORD)-1;
                dpi.dwFunctionNumber = (DWORD)-1;
                if (TSubKey.GetPciInfo(&dpi)!=ERROR_SUCCESS || dpi.dwDeviceNumber == (DWORD)-1 || dpi.dwFunctionNumber == (DWORD)-1) {
                    dpi.dwDeviceNumber = m_dwDeviceIndex++;
                    dpi.dwFunctionNumber = 0;
                }
                DWORD dwBusNumber = (DWORD)0;
                // We Create Foler for this driver.
                TCHAR lpChildPath[DEVKEY_LEN];
                _tcsncpy(lpChildPath,m_lpActiveRegPath,DEVKEY_LEN-1);
                lpChildPath[DEVKEY_LEN-2] = 0;
                DWORD dwLen=_tcslen(lpChildPath);
                lpChildPath[dwLen]=_T('\\');
                dwLen++;
                lpChildPath[dwLen]=0;
                _tcsncat(lpChildPath,ValName,DEVKEY_LEN-1-dwLen);
                lpChildPath[DEVKEY_LEN-1]=0;

                PCI_SLOT_NUMBER SlotNumber;
                SlotNumber.u.bits.DeviceNumber= dpi.dwDeviceNumber;
                SlotNumber.u.bits.FunctionNumber =  dpi.dwFunctionNumber;

                SensorFolder * nDevice =
                    new SensorFolder (m_lpBusName!=NULL?m_lpBusName:BUSNAMEUNKNOWN,
                            lpChildPath,m_dwBusType,dwBusNumber,SlotNumber,GetDeviceHandle());
                if (nDevice)
                    InsertChild(nDevice);
            }
            
        }
    }
    
    return TRUE;
}
#define MAX_TEMP_BUFFER_SIZE 0x200
BOOL QciBus::ActiveAllChildDriver()
{
    TakeReadLock();
    SensorFolder * pCurDevice = (SensorFolder *)GetDeviceList ();
    while (pCurDevice) {
        DEBUGMSG(ZONE_ENUM,(TEXT("Activate Child: Template reg path is %s\r\n"),pCurDevice->GetRegPath()));
        // Create Initial Active Registry.
        for (DWORD dwIndex=0; dwIndex< m_dwNumOfInitParam; dwIndex++) {
            BYTE tempBuffer[ MAX_TEMP_BUFFER_SIZE] ;
            DWORD dwSize=MAX_TEMP_BUFFER_SIZE;
            DWORD dwType;
            if (m_DeviceKey.IsKeyOpened() && 
                    m_DeviceKey.RegQueryValueEx(m_lpInitParamArray[dwIndex],&dwType, tempBuffer,&dwSize) ) {
                REGINI Reg;
                Reg.lpszVal = m_lpInitParamArray[dwIndex];
                Reg.dwType = dwType;
                Reg.dwLen = dwSize;
                Reg.pData = tempBuffer;
                pCurDevice->AddInitReg(1, &Reg);
                DEBUGMSG(ZONE_ENUM,(TEXT("Activate Child add %s to %s ActivePath\r\n"),m_lpInitParamArray[dwIndex],pCurDevice->GetRegPath()));                
            }
        }
        pCurDevice =(SensorFolder *) pCurDevice->GetNextDeviceFolder();
    }

    // Activate Device 
    DWORD dwCurOrder = 0;
    while (dwCurOrder != MAXDWORD) {
        DEBUGMSG(ZONE_ENUM,(TEXT("Activate Child LoaderDriver at order %d \r\n"),dwCurOrder));
        DWORD dwNextOrder = MAXDWORD;
        pCurDevice = (SensorFolder *)GetDeviceList ();
        while (pCurDevice) {
            DWORD dwDeviceLoadOrder = pCurDevice->GetLoadOrder();
            if ( dwDeviceLoadOrder == dwCurOrder)
            {
                SetLastError(0);
                BOOL rv = pCurDevice->LoadDevice();
            }
            else 
            if (dwDeviceLoadOrder> dwCurOrder  && dwDeviceLoadOrder < dwNextOrder)
                dwNextOrder = dwDeviceLoadOrder;
            pCurDevice = (SensorFolder *)pCurDevice->GetNextDeviceFolder();
        }
        dwCurOrder = dwNextOrder;
    }
    LeaveReadLock();
    return TRUE;
}

BOOL QciBus::FastIOControl(DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
                           PDWORD pdwActualOut,DeviceFolder **ppDeviceFolder )
{
    BOOL  rc   = TRUE;
    
    switch (dwCode)
    {
    case IOCTL_QCI_SET_INTERFACE:
        if (pBufIn == NULL || dwLenIn < sizeof(qci_interface_t))
            goto err;

        __try
        {
            qci_set_interface((qci_interface_t*)pBufIn);
        }
        __except( EXCEPTION_EXECUTE_HANDLER )
        {
            goto err;
        }

        break;
    case IOCTL_QCI_CLOCK_ENABLE:
    {
        if (pBufIn == NULL || dwLenIn < sizeof(BOOL))
            goto err;

        __try
        {
            qci_clock_enable(*(BOOL*)pBufIn);
        }
        __except( EXCEPTION_EXECUTE_HANDLER )
        {
            goto err;
        }

        break;
    }
    case IOCTL_QCI_ENABLE:
        qci_enable();
        break;
    case IOCTL_QCI_SET_MASTER_TIMING:
        if (pBufIn == NULL || dwLenIn < sizeof(qci_master_timing_t))
            goto err;

        __try
        {
            qci_set_master_timing((qci_master_timing_t*)pBufIn);
        }
        __except( EXCEPTION_EXECUTE_HANDLER )
        {
            goto err;
        }

        break;
    case IOCTL_QCI_SET_IMAGE_FORMAT:
        {
            if (pBufIn == NULL || dwLenIn < sizeof(int) * 2)
                goto err;

            int* in_format = (int*)pBufIn;
            int* out_format = in_format + 1;

            __try
            {
                qci_set_image_format(*in_format, *out_format);
            }        
            __except( EXCEPTION_EXECUTE_HANDLER )
            {
                goto err;
            }
        }
        break;
    case IOCTL_QCI_START_CAPTURE:
        {
            int still_skips;

            __try
            {
                still_skips = *(int*)pBufIn;
            }        
            __except( EXCEPTION_EXECUTE_HANDLER )
            {
                goto err;
            }

            capture_start(still_skips);
            break;
        }

    case IOCTL_QCI_STOP_CAPTURE:
        capture_stop();
        break;

    case IOCTL_QCI_SET_CAPTURE_CALLBACK:
        {
            if (pBufIn == NULL || dwLenIn < sizeof(callback_data_t))
                goto err;
            callback_data_t* callback_data = (callback_data_t*)pBufIn;

            __try
            {
                add_callback(callback_data);
            }
            __except( EXCEPTION_EXECUTE_HANDLER )
            {
                goto err;
            }
        }
        break;
    case IOCTL_QCI_NOTIFY_FORMAT:
        {
            if (pBufIn == NULL || dwLenIn < sizeof(format_t))
                goto err;
            format_t* format = (format_t*) pBufIn;
            prepare_format(format);
        }

    case IOCTL_QCI_SET_FRAME_FORMAT:
        {
            if (pBufIn == NULL || dwLenIn < sizeof(format_t))
                goto err;
            format_t* format = (format_t*) pBufIn;
            qci_set_frame_format(format);
        }
		
        break;
    case IOCTL_QCI_SET_IMAGE_PROC_CFG:
        __try
        {
            qci_image_proc_cfg((qci_image_proc_cfg_t*)pBufIn);
        }
        __except( EXCEPTION_EXECUTE_HANDLER )
        {
            goto err;
        }
        break;
    default:
        return DefaultBusDriver::FastIOControl(dwCode, pBufIn, dwLenIn, 
            pBufOut, dwLenOut, pdwActualOut, ppDeviceFolder);
        break;
    }

    return rc;
err:
    DEBUGMSG(ZONE_IOCTL,(L"Qcibus.DLL : IOCTL 0x%x failed for invalid parameter\r\n",dwCode));

    SetLastError(ERROR_INVALID_PARAMETER);
    return FALSE;
}


extern "C" void
QCI_Deinit (DWORD dwData)
{
    DefaultBusDriver * pBusDriver = (DefaultBusDriver*) dwData;
    if (pBusDriver != NULL) 
		delete pBusDriver;
}

extern "C" BOOL 
QCI_PowerUp(DWORD dwData)
{

    power_on();
    return TRUE;
}

extern "C" BOOL 
QCI_PowerDown(DWORD dwData)
{
    return TRUE;
}

#define FASTBUSACCESS_TAG 0xDEFBa51e
typedef struct __FASTBUSACCESSHANDLE {
    DWORD dwHandleTag;
    DefaultBusDriver * pBusDriver;
    DeviceFolder     * pDeviceFolder;    
} FASTBUSACCESSHANDLE, *PFASTBUSACCESSHANDLE ;

extern "C" HANDLE
QCI_Open(
        HANDLE  pHead,          // @parm Handle returned by COM_Init.
        DWORD   AccessCode,     // @parm access code.
        DWORD   ShareMode       // @parm share mode - Not used in this driver.
        )
{
    DefaultBusDriver * pBusDriver = (DefaultBusDriver * )pHead;
    if (pBusDriver && pBusDriver->Open(AccessCode,ShareMode)) {
        PFASTBUSACCESSHANDLE pFastAccessHandle = new FASTBUSACCESSHANDLE;
        if (pFastAccessHandle) {
            pFastAccessHandle->dwHandleTag = FASTBUSACCESS_TAG;
            pFastAccessHandle->pBusDriver = pBusDriver;
            pFastAccessHandle->pDeviceFolder = NULL ; 
            return (HANDLE)pFastAccessHandle;
        }
        else 
            pBusDriver->Close();
    }
    return NULL;
}

extern "C" BOOL
QCI_Close(HANDLE pOpenHead)
{
    PFASTBUSACCESSHANDLE pFastAccessHandle = (PFASTBUSACCESSHANDLE) pOpenHead ;
    if (pFastAccessHandle && pFastAccessHandle->dwHandleTag ==FASTBUSACCESS_TAG ) {
        pFastAccessHandle->pBusDriver->Close();
        delete pFastAccessHandle ;
    }
    return FALSE;
}

extern "C" BOOL
QCI_IOControl(HANDLE pOpenHead,
              DWORD dwCode, PBYTE pBufIn,
              DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
              PDWORD pdwActualOut)
{
    PFASTBUSACCESSHANDLE pFastAccessHandle = (PFASTBUSACCESSHANDLE) pOpenHead ;

    if (pFastAccessHandle && pFastAccessHandle->dwHandleTag ==FASTBUSACCESS_TAG )
        return pFastAccessHandle->pBusDriver->FastIOControl(dwCode,pBufIn,dwLenIn,pBufOut,dwLenOut,pdwActualOut,
            &(pFastAccessHandle->pDeviceFolder));
    return FALSE;
}

//
// driver entry point.
//
extern "C" DWORD
QCI_Init(DWORD key)
{
    QciBus * qci = new QciBus((LPCTSTR)key);
    if (qci) 
	{
        if (qci->Init())
            return  (DWORD)qci;
        else
            delete  qci;
    }
    return NULL;
}

extern "C" BOOL WINAPI
DllEntry(HINSTANCE DllInstance, INT Reason, LPVOID Reserved)
{
    switch(Reason) 
	{
    case DLL_PROCESS_ATTACH:
        DEBUGREGISTER(DllInstance);
        DEBUGMSG(ZONE_INIT,(TEXT("QciBus.DLL DLL_PROCESS_ATTACH\r\n")));
        DisableThreadLibraryCalls((HMODULE) DllInstance);
        break;
    case DLL_PROCESS_DETACH:
        DEBUGMSG(ZONE_INIT,(TEXT("QciBus.DLL DLL_PROCESS_DETACH\r\n")));
        break;
    };
    return TRUE;
}
